package com.wondertek.service.service.sys.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wondertek.common.enums.StatusEnum;
import com.wondertek.common.exception.AppException;
import com.wondertek.common.log.AutoLogUtil;
import com.wondertek.common.util.security.LoginUser;
import com.wondertek.common.util.security.SecurityUtil;
import com.wondertek.service.dto.sys.req.SysDepartAddReq;
import com.wondertek.service.dto.sys.req.SysDepartQueryReq;
import com.wondertek.service.dto.sys.req.SysDepartUpdateReq;
import com.wondertek.service.entity.sys.SysDepartPo;
import com.wondertek.service.entity.sys.SysUserDepartPo;
import com.wondertek.service.mapper.sys.SysDepartMapper;
import com.wondertek.service.service.sys.ISysDepartService;
import com.wondertek.service.service.sys.ISysUserDepartService;
import jakarta.annotation.Resource;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * @author xqs
 */
@Service
public class SysDepartServiceImpl extends
        ServiceImpl<SysDepartMapper, SysDepartPo> implements ISysDepartService {

    @Resource
    private SysDepartMapper sysDepartMapper;

    @Resource
    private ISysUserDepartService iSysUserDepartService;

    @Override
    public List<SysDepartPo> findList(SysDepartQueryReq sysDepartQueryReq) {
        LambdaQueryWrapper<SysDepartPo> wrapper = new LambdaQueryWrapper<>();
        if(ObjectUtil.isNotEmpty(sysDepartQueryReq.getDepartName())){
            wrapper.like(SysDepartPo::getDepartName,sysDepartQueryReq.getDepartName());
        }
        wrapper.orderByDesc(SysDepartPo::getCreateTime);
        wrapper.eq(SysDepartPo::getStatus, StatusEnum.ENABLE.getCode());
        wrapper.orderByDesc(SysDepartPo::getSort);
        wrapper.orderByDesc(SysDepartPo::getCreateTime);
        return this.list(wrapper);
    }

    @Override
    public List<SysDepartPo> findTree(SysDepartQueryReq sysDepartQueryReq) {
        if(StrUtil.isNotBlank(sysDepartQueryReq.getDepartName())){
            return findList(sysDepartQueryReq);
        }
        return toTree(findList(sysDepartQueryReq),"0");
    }

    private List<SysDepartPo> findDepartTree(SysDepartQueryReq sysDepartQueryReq) {
        return toTree(findList(sysDepartQueryReq),"0");
    }

    @Override
    public List<SysDepartPo> findChildrenList(String departCode) {
        List<SysDepartPo> tree = findChildrenTree(departCode);
        List<SysDepartPo> list = new ArrayList<>();
        toList(tree,list);
        return list;
    }

    private void toList(List<SysDepartPo> tree,List<SysDepartPo> list) {
        for(SysDepartPo sysDepartPo: tree){
            list.add(sysDepartPo);
            if(ObjectUtil.isNotEmpty(sysDepartPo.getChildren())){
                toList(sysDepartPo.getChildren(),list);
            }
        }
    }

    @Override
    public List<SysDepartPo> findChildrenTree(String departCode) {
        SysDepartPo departPo = this.lambdaQuery().eq(SysDepartPo::getDepartCode, departCode).one();
        List<SysDepartPo> result = new ArrayList<>();
        getChildrenTree(findDepartTree(new SysDepartQueryReq()),result,departPo.getId());
        return result;
    }

    private void getChildrenTree(List<SysDepartPo> tree ,
                                 List<SysDepartPo> result,String parentId){
        for(SysDepartPo sysDepartPo: tree){
            if(sysDepartPo.getId().equals(parentId)){
                result.add(sysDepartPo);
            }else {
               getChildrenTree(sysDepartPo.getChildren(),result,parentId);
            }
        }
    }


    @Override
    public List<SysDepartPo> findListByUserId(String userId) {
        return sysDepartMapper.findListByUserId(userId);
    }

    private List<SysDepartPo> toTree(List<SysDepartPo> listData, String parentId) {
        List<SysDepartPo> treeData = new ArrayList<>();
        Iterator<SysDepartPo> it = listData.iterator();
        while (it.hasNext()){
            SysDepartPo item =it.next();
            if (item.getParentId().equals(parentId)){
                SysDepartPo sysDepartPo = new SysDepartPo();
                BeanUtil.copyProperties(item,sysDepartPo);
                treeData.add(sysDepartPo);
                //把已经添加了的数据删除，减少迭代次数
                it.remove();
            }
        }
        for(SysDepartPo item:treeData){
            item.setChildren(toTree(listData,item.getId()));
        }
        return CollUtil.isEmpty(treeData) ? Lists.newArrayList() : treeData;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysDepartAddReq sysDepartAddReq) {
        SysDepartPo sysDepartPo = new SysDepartPo();
        BeanUtil.copyProperties(sysDepartAddReq,sysDepartPo);
        if(StrUtil.isBlank(sysDepartPo.getParentId())){
            sysDepartPo.setParentId("0");
        }
        // 限制层级
        int depth = calculateDepth(sysDepartAddReq.getParentId());
        if (depth > 5) {
            throw new AppException("depart.depth.limit");
        }
        LambdaQueryWrapper<SysDepartPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.or(i -> i.eq(SysDepartPo::getDepartName, sysDepartAddReq.getDepartName()));
//                .or(i -> i.eq(SysDepartPo::getDepartCode, sysDepartAddReq.getDepartCode()));
        long count = this.count(wrapper);
        if (count > 0) {
//            throw new AppException("code.exist");
            throw new AppException("department.name.exist");
        }
        String dictCode = RandomUtil.randomStringUpper(6);
        sysDepartPo.setDepartCode(dictCode);
        this.save(sysDepartPo);
    }

    private int calculateDepth(String parentId) {
        int depth = 1;
        while (StrUtil.isNotBlank(parentId) && !StrUtil.equals("0", parentId)) {
            SysDepartPo parentDepartment = this.getById(parentId);
            parentId = parentDepartment.getParentId();
            depth++;
        }
        return depth;
    }

    @Override
    public List<SysDepartPo> findParentTypeList(String id) {
        SysDepartPo departPo = this.getById(id);
        SysDepartPo parent = this.getById(departPo.getParentId());
        return this.lambdaQuery()
                .eq(SysDepartPo::getDepartType,parent.getDepartType())
                .eq(SysDepartPo::getStatus, StatusEnum.ENABLE.getCode())
                .list();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SysDepartUpdateReq sysDepartUpdateReq) {
        SysDepartPo departPo = this.getById(sysDepartUpdateReq.getId());
        // 只能修改上一级
        if(ObjectUtil.isNotEmpty(sysDepartUpdateReq.getParentId())){
            SysDepartPo parent = this.getById(sysDepartUpdateReq.getParentId());
//            if(Integer.parseInt(departPo.getDepartType())-1!=Integer.parseInt(parent.getDepartType())){
//                throw new AppException("只能修改直属上一级！");
//            }
        }
        if(!StrUtil.equals(departPo.getDepartName(), sysDepartUpdateReq.getDepartName())){
            LambdaQueryWrapper<SysDepartPo> wrapper = new LambdaQueryWrapper<>();
            wrapper.or(i -> i.eq(SysDepartPo::getDepartName, sysDepartUpdateReq.getDepartName()));
//                .or(i -> i.eq(SysDepartPo::getDepartCode, sysDepartUpdateReq.getDepartCode()));
            long count = this.count(wrapper);
            if (count > 0) {
//            throw new AppException("code.exist");
                throw new AppException("department.name.exist");
            }
        }
//        BeanUtil.copyProperties(sysDepartUpdateReq,departPo);
//        departPo.setUpdateBy(null);
//        departPo.setUpdateTime(null);
//        this.updateById(departPo);
        LoginUser loginUser = SecurityUtil.getLoginUser();
        this.lambdaUpdate().set(SysDepartPo::getDepartName, sysDepartUpdateReq.getDepartName())
                .set(SysDepartPo::getDescription, sysDepartUpdateReq.getDescription())
                .set(SysDepartPo::getParentId, StrUtil.isBlank(sysDepartUpdateReq.getParentId()) ? "0" : sysDepartUpdateReq.getParentId())
                .set(SysDepartPo::getUpdateBy,loginUser.getUsername())
                .set(SysDepartPo::getUpdateTime,new Date())
                .eq(SysDepartPo::getId, sysDepartUpdateReq.getId()).update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String id) {
        LambdaQueryWrapper<SysUserDepartPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUserDepartPo::getDepartId, id);
        long count = iSysUserDepartService.count(wrapper);
        if (count > 0) {
            throw new AppException("user.organization.exist");
        }
        LambdaQueryWrapper<SysDepartPo> departPoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        departPoLambdaQueryWrapper.eq(SysDepartPo::getParentId, id);
        long departCount = this.count(departPoLambdaQueryWrapper);
        if (departCount > 0) {
            throw new AppException("organization.child.exist");
        }
        this.removeById(id);
    }

    @Override
    public List<SysDepartPo> findAllListByDepartIds(List<String> departIds) {
        return sysDepartMapper.findAllListByDepartIds(departIds);
    }

    @Override
    public List<SysDepartPo> findParentListByDepartIds(List<String> departIds) {
        return sysDepartMapper.findParentListByDepartIds(departIds);
    }

    @Override
    public List<SysDepartPo> findChildListByDepartIds(List<String> departIds) {
        return sysDepartMapper.findChildListByDepartIds(departIds);
    }
}
